package com.xuecheng.media.service.jobhandler;

import com.alibaba.fastjson.JSON;
import com.aliyun.green20220302.Client;
import com.aliyun.green20220302.models.*;
import com.aliyun.teautil.models.RuntimeOptions;
import com.xuecheng.base.constant.MediaObjectAuditConstant;
import com.xuecheng.base.constant.MediaTypeConstant;
import com.xuecheng.base.exception.XCPlusBaseException;
import com.xuecheng.media.config.AliyunConfig;
import com.xuecheng.media.exception.MediaError;
import com.xuecheng.media.model.po.MediaFiles;
import com.xuecheng.media.service.IMediaFilesService;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.errors.ErrorResponseException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.FilterInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 勾新杰
 * @version 1.0
 * @description: 审核视频任务
 * @date 2025/3/13 14:58
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AuditVideoJob {

    /**
     * minio客户端
     */
    private final MinioClient minioClient;

    /**
     * 待处理任务
     */
    private final IMediaFilesService mediaFilesService;

    /**
     * 审核视频任务
     */
    @XxlJob("auditMediaJobHandler")
    public void auditVideoJobHandler() {
        // 1. 先查询未过审的视频、图片列表
        List<MediaFiles> list = mediaFilesService.lambdaQuery()
                .in(MediaFiles::getFileType, MediaTypeConstant.MEDIA_TYPE_VIDEO, MediaTypeConstant.MEDIA_TYPE_IMAGE)
                .eq(MediaFiles::getAuditStatus, MediaObjectAuditConstant.UN_AUDIT)
                .list();
        // 2. 准备线程池和计数器
        int size = Runtime.getRuntime().availableProcessors();
        ExecutorService pool = Executors.newFixedThreadPool(size);
        CountDownLatch count = new CountDownLatch(list.size());
        // 3. 遍历列表，调用阿里云接口进行审核
        list.forEach(mediaFiles -> pool.execute(() -> {
            // 3.0 利用数据库加分布式锁
            if (mediaFilesService.lambdaQuery()
                    .eq(MediaFiles::getFileId, mediaFiles.getFileId())
                    .one()
                    .getAuditStatus().equals(MediaObjectAuditConstant.AUDITING)) return;
            mediaFilesService.lambdaUpdate()
                    .eq(MediaFiles::getFileId, mediaFiles.getFileId())
                    .set(MediaFiles::getAuditStatus, MediaObjectAuditConstant.AUDITING)
                    .update();
            // 3.1 在minio中查询该文件
            try (FilterInputStream response = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(mediaFiles.getBucket())
                            .object(mediaFiles.getFilePath())
                            .build())) {
                // 3.2 构造临时文件，并将文件写入临时文件
                Path path = Files.createTempFile("temp" + UUID.randomUUID(), getSuffix(mediaFiles.getFilePath()));
                Files.copy(response, path, StandardCopyOption.REPLACE_EXISTING);
                // 3.3 准备视频审核客户端和oss客户端
                Client greenClient = AliyunConfig.getGreenClient();
                RuntimeOptions runtime = new RuntimeOptions();
                AliyunConfig.getOssClient();
                // 3.4 上传文件到oss
                String objectName = AliyunConfig.uploadFile(path.toString());
                // 3.5 检测参数构造。
                Map<String, String> serviceParameters = new HashMap<>();
                serviceParameters.put("ossBucketName", AliyunConfig.getToken().getBucketName());
                serviceParameters.put("ossObjectName", objectName);
                serviceParameters.put("dataId", UUID.randomUUID().toString());
                // 3.6 如果是视频文件，则调用阿里云视频审核接口进行审核
                if (mediaFiles.getFileType().equals(MediaTypeConstant.MEDIA_TYPE_VIDEO)) {
                    // 3.6.1 构建请求
                    VideoModerationRequest request = new VideoModerationRequest();
                    request.setService("videoDetection");
                    request.setServiceParameters(JSON.toJSONString(serviceParameters));
                    // 3.6.2 调用阿里云视频审核接口
                    VideoModerationResponse moderationResponse = greenClient.videoModerationWithOptions(request, runtime);
                    // 3.6.3 处理结果
                    if (moderationResponse != null) {
                        if (moderationResponse.getStatusCode() == 200) {
                            // 3.6.3.1 输出日志信息
                            VideoModerationResponseBody body = moderationResponse.getBody();
                            String message = body.getMessage();
                            Integer code = body.getCode();
                            log.debug(JSON.toJSONString(body));
                            log.debug("requestId = " + body.getRequestId());
                            log.debug("code = " + code);
                            log.debug("msg = " + message);
                            // 3.6.3.2 如果审核通过，则更新文件状态为审核通过，否则更新文件状态为审核失败
                            if (200 == code) {
                                VideoModerationResponseBody.VideoModerationResponseBodyData data = body.getData();
                                log.debug("taskId = [" + data.getTaskId() + "]");
                                // 更新文件状态为审核通过
                                mediaFilesService.lambdaUpdate()
                                        .eq(MediaFiles::getFileId, mediaFiles.getFileId())
                                        .set(MediaFiles::getAuditStatus, MediaObjectAuditConstant.AUDIT_SUCCESS)
                                        .set(MediaFiles::getAuditMind, null)
                                        .update();
                            }
                            // 3.6.3.3 如果审核不通过，则记录失败原因
                            else {
                                System.out.println("video moderation not success. code:" + code);
                                // 更新文件状态为审核失败并记录失败原因
                                mediaFilesService.lambdaUpdate()
                                        .eq(MediaFiles::getFileId, mediaFiles.getFileId())
                                        .set(MediaFiles::getAuditStatus, MediaObjectAuditConstant.AUDIT_FAILED)
                                        .set(MediaFiles::getAuditMind, message)
                                        .update();
                            }
                        } else {
                            log.error("moderationResponse not success. status:{}", moderationResponse.getStatusCode());
                        }
                    }
                }
                // 3.4 如果是图片文件，则调用阿里云图片审核接口进行审核
                else {
                    // 3.4.1 构建请求
                    ImageModerationRequest request = new ImageModerationRequest();
                    request.setService("baselineCheck");
                    request.setServiceParameters(JSON.toJSONString(serviceParameters));
                    // 3.4.2 调用阿里云图片审核接口
                    ImageModerationResponse moderationResponse = greenClient.imageModerationWithOptions(request, runtime);
                    // 3.4.3 处理结果
                    if (moderationResponse != null) {
                        if (moderationResponse.getStatusCode() == 200) {
                            // 3.4.3.1 输出日志信息
                            ImageModerationResponseBody body = moderationResponse.getBody();
                            String message = body.getMsg();
                            Integer code = body.getCode();
                            log.debug(JSON.toJSONString(body));
                            log.debug("requestId = " + body.getRequestId());
                            log.debug("code = " + code);
                            log.debug("msg = " + message);
                            // 3.4.3.2 如果审核通过，则更新文件状态为审核通过，否则更新文件状态为审核失败
                            if (body.getCode() == 200) {
                                // 3.4.3.2.1 打印日志
                                ImageModerationResponseBody.ImageModerationResponseBodyData data = body.getData();
                                log.debug("dataId=" + data.getDataId());
                                List<ImageModerationResponseBody.ImageModerationResponseBodyDataResult> results = data.getResult();
                                for (ImageModerationResponseBody.ImageModerationResponseBodyDataResult result : results) {
                                    log.debug("label=" + result.getLabel());
                                    log.debug("confidence=" + result.getConfidence());
                                }
                                // 3.4.3.2.2 更新文件状态为审核通过
                                mediaFilesService.lambdaUpdate()
                                        .eq(MediaFiles::getFileId, mediaFiles.getFileId())
                                        .set(MediaFiles::getAuditStatus, MediaObjectAuditConstant.AUDIT_SUCCESS)
                                        .set(MediaFiles::getAuditMind, null)
                                        .update();
                            }
                            // 3.4.3.3 如果审核不通过，则记录失败原因
                            else {
                                log.debug("image moderation not success. code:{}", body.getCode());
                                mediaFilesService.lambdaUpdate()
                                        .eq(MediaFiles::getFileId, mediaFiles.getFileId())
                                        .set(MediaFiles::getAuditStatus, MediaObjectAuditConstant.AUDIT_FAILED)
                                        .set(MediaFiles::getAuditMind, message)
                                        .update();
                            }
                        } else {
                            System.out.println("response not success. status:" + moderationResponse.getStatusCode());
                        }
                    }
                }
            } catch (ErrorResponseException e) {
                if (e.getMessage().equals("NoSuchKey")) {
                    log.error("minio中该文件不存在");
                    throw new XCPlusBaseException(MediaError.FILE_IN_MINIO_NOT_EXIST.getErrMessage());
                }
                log.error("从minio中获取文件失败, {}", e.getMessage());
                throw new RuntimeException(e);
            } catch (IOException e) {
                log.error("创建临时文件失败，原因：{}", e.getMessage());
                throw new XCPlusBaseException(MediaError.CREATE_TEMP_FILE_FAILED.getErrMessage());
            } catch (Exception e) {
                log.error("请求阿里审核接口进行审核操作失败，原因：{}", e.getMessage());
                throw new RuntimeException(e);
            } finally {
                count.countDown();
            }
        }));
    }

    /**
     * 获取文件后缀
     *
     * @param filePath 文件路径
     * @return 文件后缀
     */
    private String getSuffix(String filePath) {
        return filePath.substring(filePath.lastIndexOf("."));
    }
}
